#include <math.h>
#include <iostream>
#include <set>
#include <utility>
#include <vector>

using std::cout;
using std::endl;
using std::multiset;
using std::pair;
using std::vector;

template <typename Container>
void display(const Container &con)
{
    for(auto &elem : con)
    {
        cout << elem << "  ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {
        /* cout << "Point(int = 0, int = 0)" << endl; */
    }

    double getDistance() const
    {
        return hypot(_ix, _iy);
    }

    int getX() const
    {
        return _ix;
    }

    int getY() const
    {
        return _iy;
    }

    ~Point()
    {
        /* cout << "~Point()" << endl; */
    }

    friend std::ostream &operator<<(std::ostream &os, const Point &rhs);
    friend bool operator<(const Point &lhs, const Point &rhs);
    friend struct ComparePoint; //友元结构体
private:
    int _ix;
    int _iy;
};

std::ostream &operator<<(std::ostream &os, const Point &rhs)
{
    os << "(" << rhs._ix
       << ", " << rhs._iy
       << ")";

    return os;
}

//1、默认比较器里的运算符重载，优先级最低
bool operator<(const Point &lhs, const Point &rhs)
{
    cout << "bool operator<(const Point &, const Point &)"<<endl;
    /*
        重载的 < ，用于比较两个点的大小。
        因为默认的比较器中，
    template<
            class key
            class Compare = std::less<key>
        > class multiset;
    std::less是一个结构体，它里面重载了 operator()
    bool operator()(const T &lhs, const T &rhs) ，
    在这个重载函数里，利用了 <  符号比较lhs和rhs
    正是因为这个 < 不能比较两个点大小，所以重载<即可

    至于std::less在里面是怎么进行比较的，或者说为什么重载了()，不管我们的事
    */

    if(lhs.getDistance() < rhs.getDistance())
    {
        return true;    //说明左边确实小于右边，满足<
    }
    else if(lhs.getDistance() == rhs.getDistance())
    {
        if(lhs._ix < rhs._ix)
        {
            return true;
        }
        else if(lhs._ix == rhs._ix)
        {
            if(lhs._iy < rhs._iy)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else 
    {
        return false;
    }
}

//函数对象
//2、不用std::less，自己写一个比较器，但要先看好原先的默认比较器是怎么写的
struct ComparePoint
{
    bool operator()(const Point &lhs, const Point &rhs) const
    {
        cout << "struct ComparePoint" << endl;
        if(lhs.getDistance() < rhs.getDistance())
        {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance())
        {
            if(lhs._ix < rhs._ix)
            {
                return true;
            }
            else if(lhs._ix == rhs._ix)
            {
                if(lhs._iy < rhs._iy)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else 
        {
            return false;
        }
    }
};

//命名空间是可以进行扩展的
namespace  std
{
//3、模板的特化
//因为less结构体在std里，现在将其特化针对Point
//相当于默认使用的std::less里，有针对比较Point的方法了
template <>
struct less<Point>
{
    bool operator()(const Point &lhs, const Point &rhs) const
    {
        cout << "template <> struct less<Point>" << endl;
        if(lhs.getDistance() < rhs.getDistance())
        {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance())
        {
            if(lhs.getX() < rhs.getX())     //这里写成友元不太方便
            {
                return true;
            }
            else if(lhs.getX() == rhs.getX())
            {
                if(lhs.getY() < rhs.getY())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else 
        {
            return false;
        }
    }
};//end of namespace less

}//end of namespace std


void test()
{
    multiset<Point> number = {
    /* multiset<Point, ComparePoint> number = {  用自己写的比较器 */
        Point(1, 2),
        Point(1, -2),
        Point(-1, 2),
        Point(3, 2),
        Point(1, 2),
        Point(-2, 2),
    };
    display(number);
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}


